package com.soup.lab.third.token;

import com.soup.lab.third.ThirdInvokeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.HttpStatusCodeException;

import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

public class TokenRetryHelper {

    private static final Logger logger = LoggerFactory.getLogger(TokenRetryHelper.class);

    /**
     * 最大获取token重试次数
     */
    private static final int MAX_RETRY_TIMES = 3;

    public static <T> T doWithTokenPack(Function<TokenPack, T> command, TokenFetcher tokenFetcher, TokenPack tokenPack) {
        return doWithTokenPack(command, tokenFetcher, tokenPack, null);
    }

    public static <T> T doWithTokenPack(Function<TokenPack, T> command, TokenFetcher tokenFetcher, TokenPack tokenPack,
                                  Function<HttpStatusCodeException, T> exceptionHandler) {
        return doWithTokenPack(command, tokenFetcher, tokenPack, 0, exceptionHandler);
    }

    public static <T> T doWithTokenPack(Function<TokenPack, T> command, TokenFetcher tokenFetcher, TokenPack tokenPack,
                                        int retryTime, Function<HttpStatusCodeException, T> exceptionHandler) {
        try {
            if (tokenPack != null && !tokenPack.isTokenValid()) {
                throw new TokenInvalidException();
            }
            return command.apply(tokenPack);
        } catch (HttpClientErrorException | HttpServerErrorException e) {
            logger.error("{}接口调用失败!", tokenFetcher.getClass().getSimpleName(), e);
            if (e.getStatusCode() == HttpStatus.BAD_REQUEST) {
                if(exceptionHandler == null) {
                    return null;
                }
                return exceptionHandler.apply(e);
            } else if (e.getStatusCode() == HttpStatus.UNAUTHORIZED) {
                return retry(command, tokenFetcher, tokenPack, ++retryTime, exceptionHandler);
            }
            return null;
        } catch (ThirdInvokeException e) {
            throw e;
        } catch (TokenInvalidException e) {
            return retry(command, tokenFetcher, tokenPack, ++retryTime, exceptionHandler);
        } catch (Exception e) {
            logger.error("{}接口调用异常", tokenFetcher.getClass().getSimpleName(), e);
            throw new ThirdInvokeException("调用接口异常", e);
        }
    }

    private static <T> T retry(Function<TokenPack, T> command, TokenFetcher tokenFetcher, TokenPack tokenPack,
                               int retryTime, Function<HttpStatusCodeException, T> exceptionHandler) {
        if (tokenPack == null) {
            throw new ThirdInvokeException("token获取失败");
        }
        if (retryTime > 1) {
            if (retryTime > MAX_RETRY_TIMES) {
                throw new ThirdInvokeException("token获取失败，已重试" + MAX_RETRY_TIMES + "次");
            }
            // 独占token，防止多个地方轮番重试获取新token，导致token轮番失效而调用失败
            try {
                TimeUnit.MILLISECONDS.sleep(retryTime * 1500 + new Random().nextInt(1000));
            } catch (InterruptedException ignore) {
            }
        }
        tokenPack = tokenFetcher.packToken(tokenPack.getTokenConfig(), true);
        if (tokenPack != null && tokenPack.isTokenValid()) {
            return doWithTokenPack(command, tokenFetcher, tokenPack, retryTime, exceptionHandler);
        }
        throw new ThirdInvokeException("token获取失败");
    }




}
